#pragma once
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include "Log.hpp"
#define MAX_LISTEN 10
using namespace LogMoudle;
class Socket
{
    enum class Protocol
    {
        TCP = IPPROTO_TCP,
        UDP = IPPROTO_UDP
    };

public:
    Socket() : _socketfd(-1) {}
    Socket(int fd) : _socketfd(fd) {}
    ~Socket()
    {
        LOG(LogLevel::INFO) << _socketfd << "will be released";
        Close();
    }

    int Fd() { return _socketfd; }
    bool CreatSocket(Protocol protocol)
    {
        switch (protocol)
        {
        case Protocol::TCP:
            _socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            break;
        case Protocol::UDP:
            _socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        default:
            break;
        }

        if (_socketfd < 0)
        {
            LOG(LogLevel::ERROR) << "CreatSocket failure " << strerror(errno);
            abort();
            return false;
        }
        LOG(LogLevel::INFO) << "CreatSocket success";
        return true;
    }
    bool Bind(const std::string &ip, uint16_t port)
    {
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        addr.sin_port = htons(port);
        socklen_t len = sizeof(struct sockaddr_in);
        if (bind(_socketfd, (struct sockaddr *)&addr, len) < 0)
        {
            LOG(LogLevel::ERROR) << "bind failure " << strerror(errno);
            return false;
        }
        LOG(LogLevel::INFO) << "bind sucess";
        return true;
    }
    bool Listen(int backlog = MAX_LISTEN)
    {
        if (listen(_socketfd, MAX_LISTEN) < 0)
        {
            // lg.logmessage(Error, "listen failure %s", strerror(errno));
            LOG(LogLevel::ERROR) << "listen failure " << strerror(errno);
            return false;
        }
        // lg.logmessage(Info, "listen sucess");
        LOG(LogLevel::INFO) << "listen sucess";
        return true;
    }
    int Accept()
    {
        int fd = accept(_socketfd, nullptr, nullptr);
        if (fd < 0)
        {
            // lg.logmessage(Error, "accept failure %s", strerror(errno));
            LOG(LogLevel::ERROR) << "accept failure " << strerror(errno);
            return -1;
        }
        // lg.logmessage(Info, "accept sucess");
        LOG(LogLevel::INFO) << "accept sucess";
        return fd;
    }
    ssize_t Recive(void *buf, size_t n, int flag = 0)
    {
        ssize_t ret;
        ret = recv(_socketfd, buf, n, flag);
        if (ret <= 0)
        {
            /// EAGAIN 无数据
            /// EINTR(enter interupt)被中断打断
            if (errno == EAGAIN || errno == EINTR)
            {
                // lg.logmessage(Warning, "recive warning");
                LOG(LogLevel::WARNING) << "recive warning " << strerror(errno);
                return 0;
            }
            // lg.logmessage(Error, "recive failure %s", strerror(errno));
            LOG(LogLevel::ERROR) << "recive failure " << strerror(errno);
            return -1;
        }
        // lg.logmessage(Info, "recive suess");
        LOG(LogLevel::INFO) << "recive suess";
        return ret;
    }

    ssize_t NoneBlockRecv(void *buf, size_t n)
    {
        return Recive(buf, n, MSG_DONTWAIT);
    }
    ssize_t Send(const void *buf, size_t n, int flag = 0)
    {
        int ret;
        ret = send(_socketfd, buf, n, flag);
        if (ret < 0)
        {
            // lg.logmessage(Error, "send failure %s", strerror(errno));
            LOG(LogLevel::ERROR) << "send failure " << strerror(errno);
            return -1;
        }
        else
        {
            // lg.logmessage(Info, "send suess");
            LOG(LogLevel::INFO) << "send suess";
        }
        return ret;
    }

    ssize_t NoneBlockSend(const void *buf, size_t n)
    {

        return Send(buf, n, MSG_DONTWAIT);
    }

    bool Connect(const std::string &ip, uint16_t port)
    {
        sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = inet_addr(ip.c_str());
        server.sin_port = htons(port);
        if (connect(_socketfd, (sockaddr *)&server, sizeof(sockaddr_in)))
        {
            // lg.logmessage(Error, "connect error %s", strerror(errno));
            LOG(LogLevel::ERROR) << "connect error " << strerror(errno);
            return false;
        }
        // lg.logmessage(Info, "connect succes");
        LOG(LogLevel::INFO) << "connect suess";
        return true;
    }

    bool CreatTcpServer(uint16_t port, const std::string &ip = "0.0.0.0", bool noneBlock = false)
    {

        if (CreatSocket(Protocol::TCP))
        {
            ResuseAddrAndPort();
            if (Bind(ip, port))
            {

                if (noneBlock)
                    NoneBlockSocket();
                if (Listen())
                {

                    return true;
                }
            }
        }
        return false;
    }

    /// @brief 创建一个客户端
    /// @param ip 服务端ip
    /// @param port 服务端port
    /// @return 是否创建成功
    bool CreatTcpClient(std::string ip, uint16_t port)
    {
        if (CreatSocket(Protocol::TCP))
        {
            if (Connect(ip, port))
                return true;
        }
        return false;
    }

    /// @brief 端口地址复用
    bool ResuseAddrAndPort()
    {
        int ret = 0;
        int opt = 1;
        ret = setsockopt(_socketfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
        if (ret < 0)
        {
            // g.logmessage(Error, "setsockopt error %s", strerror(errno));
            LOG(LogLevel::ERROR) << "setsockopt error " << strerror(errno);
            return false;
        }
        opt = 1;
        ret = setsockopt(_socketfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(int));
        if (ret < 0)
        {
            // lg.logmessage(Error, "setsockopt error %s", strerror(errno));
            LOG(LogLevel::ERROR) << "setsockopt error " << strerror(errno);
            return false;
        }
        // lg.logmessage(Info, "setsockopt  success");
        LOG(LogLevel::INFO) << "setsockopt suess";
        return true;
    }

    void NoneBlockSocket()
    {
        int flag = fcntl(_socketfd, F_GETFL, 0);
        fcntl(_socketfd, F_SETFL, flag | O_NONBLOCK);
    }

    void Close()
    {
        if (_socketfd != -1)
        {
            // std::cout << "close " << _socketfd << std::endl;
            // lg.logmessage(Debug, "%s close:%d", __LINE__, _socketfd);
            close(_socketfd);
            _socketfd = -1;
            return;
        }
        // std::cout << __LINE__ << " have closed " << _socketfd << std::endl;
        // lg.logmessage(Debug, "%s have closed", __LINE__);
        _socketfd = -1;
    }

private:
    int _socketfd;
};